# Autosub Helpers.py - http://code.google.com/p/auto-sub/
#
# The Autosub helper functions
#

import logging
import re
import subprocess
from string import capwords
import time

import autosub.Db
# Settings
log = logging.getLogger('thelogger')

REGEXES = [
        re.compile("^((?P<title>.+?)[. _-]+)?s(?P<season>\d+)[x. _-]*e(?P<episode>\d+)(([. _-]*e|-)(?P<extra_ep_num>(?!(1080|720)[pi])\d+))*[. _-]*((?P<quality>(1080|720|SD))*[pi]*[. _-]*(?P<source>(hdtv|dvdrip|bdrip|blu[e]*ray|web[. _-]*dl))*[. _]*(?P<extra_info>.+?)((?<![. _-])-(?P<releasegrp>[^-]+))?)?$", re.IGNORECASE),
        re.compile("^((?P<title>.+?)[\[. _-]+)?(?P<season>\d+)x(?P<episode>\d+)(([. _-]*x|-)(?P<extra_ep_num>(?!(1080|720)[pi])\d+))*[. _-]*((?P<quality>(1080|720|SD))*[pi]*[. _-]*(?P<source>(hdtv|dvdrip|bdrip|blu[e]*ray|web[. _-]*dl))*[. _]*(?P<extra_info>.+?)((?<![. _-])-(?P<releasegrp>[^-]+))?)?$", re.IGNORECASE),
        re.compile("^(?P<title>.+?)[. _-]+(?P<season>\d{1,2})(?P<episode>\d{2})([. _-]*(?P<quality>(1080|720|SD))*[pi]*[. _-]*(?P<source>(hdtv|dvdrip|bdrip|blu[e]*ray|web[. _-]*dl))*[. _]*(?P<extra_info>.+?)((?<![. _-])-(?P<releasegrp>[^-]+))?)?$", re.IGNORECASE)
        ]
QUALITY_PARSER = re.compile("(hdtv|tv|dvdrip|dvd|bdrip|blu[e]*ray|web[. _-]*dl)", re.IGNORECASE)


def RunCmd(cmd):
    process = subprocess.Popen(cmd,
                            shell = True,
                            stdin = subprocess.PIPE,
                            stdout = subprocess.PIPE,
                            stderr = subprocess.PIPE)
    shell = process.stdout.read()
    shellerr = process.stderr.read()
    process.wait()
    return shell, shellerr


def CleanSerieName(series_name):
    """Clean up series name by removing any . and _
    characters, along with any trailing hyphens.

    Is basically equivalent to replacing all _ and . with a
    space, but handles decimal numbers in string, for example:

    >>> cleanRegexedSeriesName("an.example.1.0.test")
    'an example 1.0 test'
    >>> cleanRegexedSeriesName("an_example_1.0_test")
    'an example 1.0 test'

    Stolen from dbr's tvnamer
    """
    try:
        series_name = re.sub("(\D)\.(?!\s)(\D)", "\\1 \\2", series_name)
        series_name = re.sub("(\d)\.(\d{4})", "\\1 \\2", series_name)  # if it ends in a year then don't keep the dot
        series_name = re.sub("(\D)\.(?!\s)", "\\1 ", series_name)
        series_name = re.sub("\.(?!\s)(\D)", " \\1", series_name)
        series_name = series_name.replace("_", " ")
        series_name = re.sub("-$", "", series_name)
        return capwords(series_name.strip())
    except TypeError:
        log.debug("CleanSerieName: There is no SerieName to clean")


def ReturnUpper(text):
    """
    Return the text converted to uppercase.
    When not possible return nothing.
    """
    try:
        text = text.upper()
        return text
    except:
        pass


def ProcessFileName(filename, extension):
    processedFilenameResults = {}
    title = None       # The Show Name
    season = None      # Season number
    episode = None     # Episode number
    quality = None     # quality, can either be 1080, 720 or SD
    releasegrp = None  # The Release group
    source = None      # The source, can either be hdtv, dvdrip, bdrip, blueray or web-dl
    matchdic = {}

    filename = filename.lower()
    # Try to determine the TV Episode information
    for regexes in REGEXES:  # Lets try all the regexpresions in REGEXES
        matches = regexes.search(filename)
        try:
            matchdic = matches.groupdict()
            log.debug("ProcessFileName: Hit with a regex, dumping it for debug purpose: " + str(matchdic))
            break  # If we got a match, lets break
        except AttributeError:
            continue
    # Trying to set all the main attributes
    if 'title' in matchdic.keys(): title = CleanSerieName(matchdic["title"])
    if 'season' in matchdic.keys(): season = matchdic["season"]
    if 'episode' in matchdic.keys(): episode = matchdic["episode"]
    if 'source' in matchdic.keys():
        source = matchdic["source"]
        if source != None:
            source = re.sub("[. _-]", "-", source)
    if 'releasegrp' in matchdic.keys(): releasegrp = matchdic["releasegrp"]
    if 'quality' in matchdic.keys(): quality = ReturnUpper(matchdic["quality"])  # Quality should be upper case, in case the quality is SD

    # Fallback for the quality and source mkv files and mp4 are most likely HD quality
    # Other files are more likely sd quality
    # Will search the file for a couple of possible sources, also parse out dots and dashes and replace with a dash
    if source == None:
        results = re.search(QUALITY_PARSER, filename)
        try:
            source = results.group(0)
            source = re.sub("[. _-]", "-", source)
            # The following four rules are there to support the file naming SickBeard used (like: Serie.Name.S01E02.SD.TV.avi
            if source == 'tv':
                source = 'hdtv'
            if source == 'dvd':
                source = 'dvdrip'
            log.debug("ProcessFileName: Fallback hit for source, source is %s" % source)
        except:
            pass

    if quality == None:
        if extension == ".mkv":
            quality = '720'
            log.debug("ProcessFileName: Fallback, file seems to be mkv or mp4 so best guess for quality would be 720")
        else:
            quality = 'SD'
            log.debug("ProcessFileName: Fallback, can't determine the quality so guess SD")

    if title: processedFilenameResults['title'] = title
    if season: processedFilenameResults['season'] = season
    if episode: processedFilenameResults['episode'] = episode
    if quality: processedFilenameResults['quality'] = quality
    if source: processedFilenameResults['source'] = source
    if releasegrp: processedFilenameResults['releasegrp'] = releasegrp
    log.debug("ProcessFileName: Returning %s" % processedFilenameResults)

    return processedFilenameResults


def matchQuality(quality, item):
    if quality == "SD":
        if re.search('720', item):
            log.debug("matchQuality: Quality SD did not match to %s" % item)
            return None
        elif re.search('1080', item):
            log.debug("matchQuality: Quality SD did not match to %s" % item)
            return None
        else:
            log.debug("matchQuality: Quality matched SD to %s" % item)
            return 1
    elif quality == "1080" and re.search('1080', item):
        log.debug("matchQuality: Quality is 1080 matched to %s" % item)
        return 1
    elif quality == "720" and re.search('720', item):
        log.debug("matchQuality: Quality is 720 matched to %s" % item)
        return 1


def scoreMatch(release, quality, releasegrp, source):
    """
    Return how high the match is. Currently 7 is the best match
    This function give the flexibility to change the most important attribute for matching or even give the user the possibility to set his own preference
    release is the filename as it is in the result from bierdopje
    If quality is matched, score increased with 2
    If releasegrp is matched, score is increased with 1
    If source is matched, score is increased with 4
    """
    score = 0
    log.debug("scoreMatch: Giving a matchscore for: %s. Try to match it with Q: %s GRP: %s S: %s" % (release, quality, releasegrp, source))
    if releasegrp:
        if (re.search(releasegrp, release, re.IGNORECASE)):
            score += 1
    if source:
        if (re.search(source, release, re.IGNORECASE)):
            score += 4
    if quality:
        if (matchQuality(quality, release)):
            score += 2
    log.debug("scoreMatch: MatchScore is %s" % str(score))
    return score

def SkipShow(showName, season, episode):
    if showName.upper() in autosub.SKIPSHOWUPPER.keys():
        log.debug("SkipShow: Found %s in skipshow dictonary" % showName)
        for seasontmp in autosub.SKIPSHOWUPPER[showName.upper()]:
            if seasontmp == '0':
                log.debug("SkipShow: variable of %s is set to 0, skipping the complete Serie" % showName)
                return True
            elif int(seasontmp) == int(season):
                log.debug("SkipShow: Season matches variable of %s, skipping season" % showName)
                return True

def GetShowId(showName, useAPI = True):
    """
    Returns the bierdopje show id.
    
    Keyword arguments:
    showName -- the local showname, parsed by scanDisk. Str
    useAPI -- if you set useAPI to false, GetShowId will not use the bierdopje API,
    use it in your function if you are already checking the API for something else.
    """
    
    dbconnection = autosub.Db.dbCon()
    log.debug("GetShowId: Trying to get a ShowID for %s and UseAPI = %s" %(showName.upper(), str(useAPI)) )
    databasecursor = dbconnection.runQuery('select bierdopje_id from localname where LocalName = ?', [showName.upper()])
    for row in databasecursor:
        showId = int(row[0])
        log.debug("GetShowId: Found show ID in localname %d" %showId)
        return showId
    
    databasecursor = dbconnection.runQuery('select bierdopje_id from id_cache where ShowName = ?', [showName.upper()])
    for row in databasecursor:
        showId = int(row[0])
        log.debug("GetShowId: Found show ID in id_cache %d" %showId)
        return showId
    if useAPI:
        log.debug("GetShowId: Trying to find a showId using the API")
        showId = autosub.Bierdopje.getShowid(showName)
        if not showId:
            log.error("GetShowId: Could not find showId for: %s" %showName.upper())
            dbconnection.runQuery('insert into id_cache values (?,?)',[-1,showName])
            return -1
        else:
            dbconnection.runQuery('insert into id_cache values (?,?)',[showId,showName.upper()])
            log.debug("GetShowId: Found show ID using the API %d added to DB" %showId)
            return showId

def GetShowInfo(showName = None, showId = None, showPath = None):
    """
    Returns dict containing showinformation.

    Keyword arguments:
    showName -- the local showname, parsed by scanDisk. Str
    showId -- the bierdopje showid. Int
    """
    dbconnection = autosub.Db.dbCon()
    showInfo = None
    showInfoRow = None
    if showName:
        log.info("GetShowInfo: Trying to get show info by using the showname: %s" %showName)
        showId = GetShowId(showName, useAPI = False)
        if not showId:
            log.debug("GetShowInfo: No ID found in cache or localname trying database.")
            databasecursor = dbconnection.runQuery('select * from tv_shows where show_name = ?', [showName.upper()])
            for row in databasecursor:
                showInfoRow = row
            if showInfoRow:
                showInfo = {}
                showInfo['showid'] = int(showInfoRow[1])
                showInfo['tvdbid'] = int(showInfoRow[2])
                showInfo['showname'] = str(showInfoRow[3])
                showInfo['showlink'] = str(showInfoRow[4])
                showInfo['status'] = str(showInfoRow[5])
                showInfo['pathtoshow'] = str(showInfoRow[6])
                log.debug("GetShowInfo: Got showinfo from tv_shows database %s" %showInfo)
                return showInfo
            
            if not showInfo:
                log.debug("GetShowInfo: No showinfo found in local database, using bierdopje API .")
                showInfo = autosub.Bierdopje.getShowInfo(showName=showName)
                if showInfo:
                    log.debug("GetShowInfo: Got showinfo %s" %showInfo)
                    showInfoList = []
                    showInfoList.append(showInfo['showid'])
                    showInfoList.append(showInfo['tvdbid'])
                    showInfoList.append(showInfo['showname'])
                    showInfoList.append(showInfo['showlink'])
                    showInfoList.append(showInfo['status'])
                    dbconnection.runQuery('insert into id_cache values (?,?)',[showInfo['showid'],showName])
                    
                    if autosub.DOWNLOADENG == True:
                        enwanted = 1
                    elif autosub.FALLBACKTOENG == True:
                        enwanted = 4 
                    else:
                        enwanted = 0
                    nlwanted = 1
                    
                    dbconnection.runQuery('insert into tv_shows values (NULL,?,?,?,?,?,?,?,?)',[showInfo['showid'], 
                                                                                                showInfo['tvdbid'], 
                                                                                                showInfo['showname'], 
                                                                                                showInfo['showlink'], 
                                                                                                showPath, 
                                                                                                showInfo['status'], 
                                                                                                nlwanted, 
                                                                                                enwanted])
                    return showInfo
                else:
                    log.debug("GetShowInfo: No showinfo found on bierdopje for %s" %showName)
                    dbconnection.runQuery('insert into id_cache values (?,?)',[-1,showName])
                    log.debug("GetShowInfo: Info added to cache, returning -1")
                    return -1
        
    if showId:
        log.info("GetShowInfo: Trying to get show info by using the showid: %d" %showId)
        if showId == -1:
            log.debug("GetShowInfo: the showid == -1, so lets skip it.")
            return -1
        else:
            databasecursor = dbconnection.runQuery('select * from tv_shows where bierdopje_id = ?', [showId])
            for row in databasecursor:
                showInfoRow = row
            if showInfoRow:
                showInfo = {}
                showInfo['showid'] = int(showInfoRow[1])
                showInfo['tvdbid'] = int(showInfoRow[2])
                showInfo['showname'] = str(showInfoRow[3])
                showInfo['showlink'] = str(showInfoRow[4])
                showInfo['pathtoshow'] = str(showInfoRow[5])
                showInfo['status'] = str(showInfoRow[6])
                log.debug("GetShowInfo: Got showinfo from tv_shows database %s" %showInfo)
                return showInfo
            
            if not showInfo:
                log.debug("GetShowInfo: using the bierdopje API to get showinfo for %d" %showId)
                showInfo = autosub.Bierdopje.getShowInfo(showId = showId)
                if showInfo:
                    log.debug("GetShowInfo: dumping showInfo for debug %s" %showInfo)
                    return showInfo
                else:
                    log.debug("GetShowInfo: No showinfo found on bierdopje for %d" %showId)
                    return -1

def checkAPICalls(use=False):
    """
    Checks if there are still API calls left
    Set true if a API call is being made.
    """
    currentime = time.time()
    lastrun = autosub.APIMAXLASTRESET
    interval = autosub.APIMAXRESETEVERY
    
    if currentime - lastrun > interval:
        autosub.APICALLS = autosub.APIMAXCALLS
        autosub.APIMAXLASTRESET = time.time()
    
    if autosub.APICALLS > 0:
        if use==True:
            autosub.APICALLS-=1
        return True
    else:
        return False

def getWantedLang(showid):
    dbconnection = autosub.Db.dbCon()
    databasecursor = dbconnection.runQuery('select nlwanted,enwanted from tv_shows where show_id = ?', [showid])
    langs = []
    for row in databasecursor:
        langs.append(row[0])
        langs.append(row[1])
    return langs

def getLocalShowId(showid, path=False):
    dbconnection = autosub.Db.dbCon()
    log.debug('getLocalShowId: Trying to get a localshowid for %d for path: %s' %(showid,path))
    if path:
        databasecursor = dbconnection.runQuery('select show_id from tv_shows where bierdopje_id = ? and LocationOnDisk = ?', [showid,path])
        
        for row in databasecursor:
            localshowid = int(row[0])
            log.debug('getLocalShowId: Got localshow id localshowid for %d' %localshowid)
            return localshowid
    else:
        databasecursor = dbconnection.runQuery('select show_id from tv_shows where bierdopje_id = ?', [showid])
        for row in databasecursor:
            localshowid = int(row[0])
            log.debug('getLocalShowId: Got localshow id localshowid for %d' %localshowid)
            return localshowid
    log.debug('getLocalShowId: No localshowid found')
    return -1

def getBierdopjeIDLocalID(localshowid):
    """
    Input local id
    Returns bierdopje id
    """
    dbconnection = autosub.Db.dbCon()
    databasecursor = dbconnection.runQuery('select bierdopje_id from tv_shows where show_id = ?', [localshowid])
    for row in databasecursor:
        showid = int(row[0])
        return showid
    return '-1'

def getShowName(localshowid):
    """
    Input should be localshowid
    """
    dbconnection = autosub.Db.dbCon()
    databasecursor = dbconnection.runQuery('select show_name from tv_shows where show_id = ?', [localshowid])
    for row in databasecursor:
        showname = row[0]
        return showname
    return '-1'
        
def ReadDb():
    wantedList = []
    dbconnection = autosub.Db.dbCon()
    databasecursor = dbconnection.runQuery('select * from episodes', [])
    for row in databasecursor:
        episodeid = row[0]
        localshowid = row[1]
        episode = row[2]
        season = row[3]
        source = row[4]
        quality = row[5]
        releasegrp = row[6]
        nlwanted = row[7]
        enwanted = row[8]
        downloadedsubnl = row[9]
        downloadedsuben = row[10]
        localvideofile = row[11]
        nlsrtfile = row[12]
        ensrtfile = row[13]
        airdate = row[14]
        
        wantedItem = {}
        
        wantedItem['episodeid'] = episodeid
        wantedItem['localshowid'] = localshowid
        wantedItem['showid'] = getBierdopjeIDLocalID(localshowid) 
        wantedItem['season'] = str(season)
        wantedItem['episode'] = str(episode)
        if not source == 'NULL':
            wantedItem['source'] = source
        if not quality == 'NULL':
            wantedItem['quality'] = quality
        if not releasegrp == 'NULL':
            wantedItem['releasegrp'] = releasegrp
        wantedItem['nlwanted'] = nlwanted
        wantedItem['enwanted'] = enwanted
        wantedItem['videofile'] = localvideofile
        wantedItem['nlsrtfile'] = nlsrtfile
        wantedItem['ensrtfile'] = ensrtfile
        wantedItem['downloadedsubnl'] = downloadedsubnl
        wantedItem['downloadedsuben'] = downloadedsuben
        
        if not nlwanted == 0: 
            wantedList.append(wantedItem)
        elif not enwanted == 0:
            wantedList.append(wantedItem)
        
        log.debug("makeWantedQueue: dumping %s" %wantedItem)
    return wantedList

def addSnatched(episodeid, downloadlink, lang):
    dbconnection = autosub.Db.dbCon()
    if lang == 'nl':
        dbconnection.runQuery('update episodes set nlwanted = 2, downloadedsubnl = ? where episode_id = ?',[downloadlink,episodeid])
    elif lang == 'en':
        dbconnection.runQuery('update episodes set enwanted = 2, downloadedsuben = ? where episode_id = ?',[downloadlink,episodeid])
        
def updateStatus(episodeid, status, lang):
    dbconnection = autosub.Db.dbCon()
    if lang == 'nl':
        dbconnection.runQuery('update episodes set nlwanted = ? where episode_id = ?',[status,episodeid])
    elif lang == 'en':
        dbconnection.runQuery('update episodes set enwanted = ? where episode_id = ?',[status,episodeid])